package com.mingqijia.gassafety.authority.dal.provider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mingqijia.gassafety.authority.dal.command.*;
import com.mingqijia.gassafety.authority.dal.handler.ConfigureTranslator;
import com.mingqijia.gassafety.db.entity.OperateRecord;
import com.mingqijia.gassafety.db.entity.auth.DictionaryItemEntry;
import com.mingqijia.gassafety.db.entity.auth.ParamConfigEntry;
import com.mingqijia.gassafety.db.mapper.OperateRecordMapper;
import com.mingqijia.gassafety.db.mapper.auth.DictionaryItemMapper;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.cqrs.QueryHandler;
import com.mingqijia.gassafety.shared.dto.LogVO;
import com.mingqijia.gassafety.shared.enums.ParamType;
import com.mingqijia.gassafety.shared.enums.RunningState;
import com.mingqijia.gassafety.shared.enums.YesNoEnum;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

import static com.mingqijia.gassafety.shared.constant.Constants.OPERATE_RESULT_SUCCESS;

/**
 * 配置信息CU操作(C-create,U-update)
 * @author Damon S.
 * @version v1.0.1
 * @date 2021年04月12日 13:30
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DictionaryProvider extends ServiceImpl<DictionaryItemMapper, DictionaryItemEntry> implements QueryHandler<DictionaryItemEntry> {

    private final ConfigureTranslator translator;
    private final ConfigureProvider provider;
    @Autowired
    OperateRecordMapper operateRecordMapper;

    public Integer checkDuplication(String dictCode, String alias, String value) {
        Assert.notNull(dictCode, "dictCode不能为空");
        LambdaQueryWrapper<DictionaryItemEntry> queryClause = new LambdaQueryWrapper<>();
        if (StringUtils.hasText(value)) {
            queryClause.clear();
            queryClause.eq(DictionaryItemEntry::getValue, value)
                    .eq(DictionaryItemEntry::getDictCode, dictCode)
                    .eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());
            if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
                return 2;
            }
        }
        if (StringUtils.hasText(alias)) {
            queryClause.clear();
            queryClause.eq(DictionaryItemEntry::getAlias, alias)
                    .eq(DictionaryItemEntry::getDictCode, dictCode)
                    .eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());
            if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
                return 1;
            }
        }
        return 0;
    }

    public Page<DictionaryItemEntry> find(FindDictionaryItemCommand command) {
        // 查询条件
        LambdaQueryWrapper<DictionaryItemEntry> queryClause = new LambdaQueryWrapper<>();
        Optional.ofNullable(command.getDictCode()).ifPresent(dictCode -> {
            checkDictCodeValidById(command);
            queryClause.eq(DictionaryItemEntry::getDictCode, dictCode);
        });
        Optional.ofNullable(command.getName()).ifPresent(
                alias -> queryClause.like(DictionaryItemEntry::getAlias, alias)
        );
        queryClause.eq(DictionaryItemEntry::getSpId, command.getSpId())
                .eq(DictionaryItemEntry::getIsDeleted, YesNoEnum.N.getCode());
//        return this.list(queryClause.orderByAsc(DictionaryItemEntry::getOrdinal))
        return this.page(createPage(command), queryClause.orderByAsc(DictionaryItemEntry::getCreatedAt));
    }

    public List<DictionaryItemEntry> findItemsByDictCodes(String spId, Set<String> codes) {
        log.info("findItemsByDictCodes params:{},{}",spId,codes);
        QueryWrapper<DictionaryItemEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(DictionaryItemEntry::getSpId, spId)
                .in(DictionaryItemEntry::getDictCode, codes)
                .eq(DictionaryItemEntry::getIsDeleted, YesNoEnum.N.getCode());
        return this.list(queryClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public void duplicateDictionaries2Org(String spId) {
//        if (provider.checkConfigExistsBySpId(spId)) {
//            throw new GlobalAuthenticationException("该企业字典已执行过初始化");
//        }
        QueryParamConfigCommand command = QueryParamConfigCommand.builder()
                .spId(Constants.PLATFORM_SP).state(RunningState.RUNNING)
                .type(ParamType.DICTIONARY).pageSize(Constants.LONG_1_KILO)
                .configurable(YesNoEnum.Y).page(Constants.LONG_ONE).build();
        Page<ParamConfigEntry> dictionaries = provider.find(command);

        QueryParamConfigCommand command2 = QueryParamConfigCommand.builder()
                .spId(spId).state(RunningState.RUNNING)
                .type(ParamType.DICTIONARY).pageSize(Constants.LONG_1_KILO)
                .configurable(YesNoEnum.N).page(Constants.LONG_ONE).build();
        Page<ParamConfigEntry> dictionaries2 = provider.find(command2);

        Date current = Date.from(Instant.now());
        dictionaries.getRecords().forEach(record -> {
            record.setConfigurable(YesNoEnum.N.getCode());
            record.setCreatedAt(current);
            record.setSpId(spId);
            record.setScope(spId);
            record.setCreatedBy(ApplicationUtils.getUserId());
            record.setUpdatedAt(null);
            record.setUpdatedBy(null);
            record.setId(null);
            dictionaries2.getRecords().forEach(record2 -> {
                //相同时，做更新
                if (record.getCode().equals(record2.getCode())) {
                    record.setId(record2.getId());
                }
            });
            log.info("做更新或新增，id={},内容={}：",record.getId(),record.toString());
        });
        provider.saveOrUpdateBatch(dictionaries.getRecords());

        List<DictionaryItemEntry> itemEntries = this.findItemsByDictCodes(Constants.PLATFORM_SP,
                dictionaries.getRecords().stream().map(ParamConfigEntry::getCode).collect(Collectors.toSet())
        );

        List<DictionaryItemEntry> itemEntries2 = this.findItemsByDictCodes(spId,
                dictionaries.getRecords().stream().map(ParamConfigEntry::getCode).collect(Collectors.toSet())
        );

        itemEntries.forEach(item -> {
            item.setSpId(spId);
            item.setCreatedAt(current);
            item.setCreatedBy(ApplicationUtils.getUserId());
            item.setUpdatedAt(null); item.setUpdatedBy(null);
            item.setId(null);
            itemEntries2.forEach(item2 -> {
                //相同时，做更新
                if (item.getDictCode().equals(item2.getDictCode()) && item.getValue().equals(item2.getValue())) {
                    item.setId(item2.getId());
                }
            });
            log.info("做更新或新增，id={},内容={}：",item.getId(),item.toString());
        });
        this.saveOrUpdateBatch(itemEntries);
    }

    @Transactional(rollbackFor = Exception.class)
    public String create(CreateDictionaryItemCommand command) {
        Assert.notNull(command.getDictCode(), "Code不能为空");
        log.info("command为{}", command);
        if (ObjectUtils.isEmpty(command.getOrdinal())) {
            command.setOrdinal(Constants.INT_ZERO);
        }
        this.attachOperator2Command(command);
        QueryWrapper<DictionaryItemEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(DictionaryItemEntry::getSpId, command.getSpId())
                .eq(DictionaryItemEntry::getDictCode, command.getDictCode())
                .eq(DictionaryItemEntry::getIsDeleted, YesNoEnum.N.getCode())
                .and(qw -> qw.eq(DictionaryItemEntry::getValue, command.getValue())
                        .or().eq(DictionaryItemEntry::getAlias, command.getName()))
        .eq(!StringUtils.isEmpty(command.getCompanyName()), DictionaryItemEntry::getCompanyName, command.getCompanyName());
        if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause)) ) {
            throw new GlobalAuthenticationException("字典类型已存在");
        }
        DictionaryItemEntry entry = translator.toEntry(command);
        // 执行数据保存
        if (!this.save(entry)) {
            throw new GlobalAuthenticationException("字典项创建失败");
        }
        //企业级操作日志
        String typeName=operateRecordMapper.selectNameByCode(command.getDictCode());
        OperateRecord operateRecord = new OperateRecord();
        operateRecord.setOperateType(34);
        operateRecord.setDetail("新增数据字典【"+typeName+"】中的类型【"+command.getName()+"】");
        operateRecord.setOperatorCode(ApplicationUtils.getWorkingUser().getUsername());
        operateRecord.setOperatorName(ApplicationUtils.getWorkingUser().getName());
        operateRecord.setSpId(ApplicationUtils.getWorkingSpId());
        operateRecord.setOperateResult(OPERATE_RESULT_SUCCESS);
        operateRecord.setMenu("dictionariesInfo");
        operateRecord.setCommonInfo(typeName );
        operateRecord.setCreatedAt(new Date());
        operateRecord.setUpdatedAt(new Date());
        operateRecordMapper.insert(operateRecord);
        return entry.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean update(UpdateDictionaryItemCommand command) {
        Assert.notNull(command.getId(), "itemId不能为空");
        this.attachOperator2Command(command);
        log.info("command为{}", command);

        QueryWrapper<DictionaryItemEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(DictionaryItemEntry::getId, command.getId())
                .eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());

        if (this.count(queryClause) < Constants.INT_ONE) {
            throw new GlobalAuthenticationException("本企业下不存在该配置");
        }
        //重复校验
        LambdaQueryWrapper<DictionaryItemEntry> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId())
                .eq(DictionaryItemEntry::getDictCode, command.getCode())
                .eq(DictionaryItemEntry::getIsDeleted, YesNoEnum.N.getCode())
                .and(qw -> qw.eq(DictionaryItemEntry::getValue, command.getValue())
                        .or().eq(DictionaryItemEntry::getAlias, command.getName()))
                .ne(DictionaryItemEntry::getId, command.getId())
                .eq(!StringUtils.isEmpty(command.getCompanyName()), DictionaryItemEntry::getCompanyName, command.getCompanyName());
        if (!Objects.equals(Constants.INT_ZERO, this.count(lqw))) {
            throw new GlobalAuthenticationException("字典类型已存在");
        }
        //更新
        UpdateWrapper<DictionaryItemEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().eq(DictionaryItemEntry::getId, command.getId())
                .eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());

        DictionaryItemEntry entry = translator.toEntry(command);
        return this.update(entry, updateClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean remove(RemoveDictionaryItemCommand command) {
        this.attachOperator2Command(command);

        LambdaQueryWrapper<DictionaryItemEntry> queryClause = new LambdaQueryWrapper<>();
        if (!CollectionUtils.isEmpty(command.getItemIds())) {
            queryClause.in(DictionaryItemEntry::getId, command.getItemIds());
        }
        Optional.ofNullable(command.getDictCode()).ifPresent(
                dictCode -> queryClause.eq(DictionaryItemEntry::getDictCode, dictCode)
        );
        queryClause.eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());
        if (Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
            return true;
        }

        LambdaUpdateWrapper<DictionaryItemEntry> updateClause = new LambdaUpdateWrapper<>();
        if (!CollectionUtils.isEmpty(command.getItemIds())) {
            updateClause.in(DictionaryItemEntry::getId, command.getItemIds());
        }
        Optional.ofNullable(command.getDictCode()).ifPresent(
                dictCode -> updateClause.eq(DictionaryItemEntry::getDictCode, dictCode)
        );
        updateClause.eq(DictionaryItemEntry::getSpId, ApplicationUtils.getHeaderSpId());
        updateClause.set(DictionaryItemEntry::getIsDeleted, YesNoEnum.Y.getCode())
                .set(DictionaryItemEntry::getUpdatedBy, command.getUpdatedBy());

        return this.update(updateClause);
    }


    private void checkDictCodeValidById(FindDictionaryItemCommand command) {
        LambdaQueryWrapper<ParamConfigEntry> queryClause = new LambdaQueryWrapper<>();
        Optional.ofNullable(command.getDictState()).ifPresent(
                state -> queryClause.eq(ParamConfigEntry::getState, state.getCode())
        );
        Optional.ofNullable(command.getDictCode()).ifPresent(
                dictCode -> queryClause.eq(ParamConfigEntry::getCode, dictCode)
        );
        queryClause.eq(ParamConfigEntry::getSpId, command.getSpId())
                .eq(ParamConfigEntry::getIsDeleted, YesNoEnum.N.getCode());
        List<ParamConfigEntry> entries = provider.list(queryClause);

        if (CollectionUtils.isEmpty(entries) || entries.size() > Constants.INT_ONE) {
            log.warn("无对应数据或参数不合法[spId={}, code={}]", command.getSpId(), command.getDictCode());
            throw new GlobalAuthenticationException("无对应数据或参数不合法");
        }
        ParamConfigEntry entry = entries.get(Constants.INT_ZERO);
        if (ObjectUtils.isEmpty(entry)) {
            log.warn("字典不存在或无权限[spId={}, code={}]", command.getSpId(), command.getDictCode());
            throw new GlobalAuthenticationException("字典不存在或无权限");
        }
        // 对比参数是否匹配
        if (StringUtils.hasText(command.getDictCode()) &&
                !Objects.equals(entry.getCode().toLowerCase(), command.getDictCode().toLowerCase())) {
            log.warn("获取参数配置请求参数错误[spId={}, code={}]", command.getSpId(), command.getDictCode());
            throw new GlobalAuthenticationException("请求参数错误");
        }
    }
}
